#include "config.h"

//Libraries
#include "logger.h"

//QT
#include <QFile>

Config *Config::m_instance = 0;

/**
  * Loads and initializes configuration
  */
void Config::load(const QStringList *argList) {
    //Mark config as loaded, if anything fails, unmark it
    this->loaded = true;

    //First load cmd line arguments
    this->readLine(argList);
    //Now we know where to write messages
    Logger *logger = Logger::instance();
    //Check level of logging
    bool logLevelOk = true;
    if (this->lineValues["loglevel"].isEmpty()) {
        logger->setLevel(Logger::NOTICE);
    } else {
        int logLevel = this->lineValues["loglevel"].toInt(&logLevelOk);
        if (logLevelOk && logLevel > 0 && logLevel < 5) {
            logger->setLevel(logLevel);
        } else {
            logger->setLevel(Logger::NOTICE);
        }
    }
    if (this->lineValues["log"].isEmpty()) {
        //No log set up, using cmd
        static QTextStream textStream(stdout);
        logger->setOutput(&textStream);
    } else {
        //Log file set up, try to open it for write only
        static QFile file(this->lineValues["log"]);
        if (!file.open(QFile::WriteOnly | QFile::Text)) {
            //Can't open it, fall back to cmd
            static QTextStream textStream(stdout);
            logger->setOutput(&textStream);
            logger->log("Can't open log file, falling back to cmd log.", Logger::WARNING);
        } else {
            //Ok, file opened for logging
            static QTextStream textStream(&file);
            logger->setOutput(&textStream);
        }
    }
    if (!logLevelOk) logger->log("Wrong loglevel value, falling back to NOTICE(2)", Logger::WARNING);
    logger->log("CMD line arguments parsed.", Logger::NOTICE);

    //Now we can process configuration file
    QString configFile = this->lineValues["config"].isEmpty() ? "configuration.xml" : this->lineValues["config"];
    if (!this->readConfig(configFile)) {
        //We cannot load configuration file
        this->loaded = false;
    } else {
        logger->log("Configuration file parsed.", Logger::NOTICE);
    }
}

/**
  * Returns value of key
  */
QString Config::getValue(QString key) {
    if (this->configValues[key].isEmpty()) {
        return false;
    }
    return this->configValues[key];
}

/**
  * Reads arguments from cmd (given in *argList)
  */
bool Config::readLine(const QStringList *argList)
{
    for (int i = 0; i < argList->size(); i++) {

        if (argList->at(i) == "--config") {
            //Check for config parameter, it changes config file
            if ((i+1) < argList->size()) {
                this->lineValues["config"] = argList->at(i+1);
            }
        } else if (argList->at(i) == "--debug") {
            //Check for debug parameter, it causes many more messages will be written :)
            this->lineValues["debug"] = "yes";

        } else if (argList->at(i) == "--logfile") {
            //Check for log parameter, it causes all mesages to be written in file instead of console :)
            if ((i+1) < argList->size()) {
                this->lineValues["log"] = argList->at(i+1);
            }
        } else if (argList->at(i) == "--loglevel") {
            //Check for log parameter, it causes all mesages to be written in file instead of console :)
            if ((i+1) < argList->size()) {
                this->lineValues["loglevel"] = argList->at(i+1);
            }
        }
    }

    return true;
}

/**
  * Reads configuration file (given in location)
  */
bool Config::readConfig(QString location) {
    //Prepare log
    Logger *logger = Logger::instance();

    QFile file(location);

    if (!file.open(QFile::ReadOnly | QFile::Text)) {
        logger->log("Can't open configuration file.",Logger::ERROR);
        return false;
    }

    //Ok, file opened, try to read configuration
    QDomDocument domDocument;
    //error variables
    QString errorStr;
    int errorLine;
    int errorColumn;

    if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn))
    {
        QString error;
        QTextStream(&error) << "Config parse error at line " << errorLine << " column " << errorColumn << " : " << errorStr;
        logger->log(error,Logger::ERROR);
        return false;
    }

    QDomElement root = domDocument.documentElement();
    if (root.tagName() != "messenger-config") {
        logger->log("Configuration file is not correct API Messenger file.",Logger::ERROR);
        return false;
    } else if (root.hasAttribute("version") && root.attribute("version") != "1.0") {
        logger->log("The file is not an messenger configuration version 1.0 file.",Logger::ERROR);
        return false;
    }

    QDomElement child = root.firstChildElement("section");
    while (!child.isNull()) {
        parseSectionElement(child);
        child = child.nextSiblingElement("section");
    }

    return true;
}

/**
  * Parses section in configuration file
  */
void Config::parseSectionElement(const QDomElement &element)
{
    QDomElement child = element.firstChildElement();
    while (!child.isNull()) {
        if (child.tagName() == "section") {
            parseSectionElement(child);
        } else if (child.tagName() == "config") {
            QString title = child.firstChildElement("title").text();
            QString value = child.attribute("value");
            if (!title.isEmpty()) {
                this->configValues[title] = value;
            }
        }
        child = child.nextSiblingElement();
    }
}

